package xfix;

import xfix.fitness.xbi.XbiFitnessFunction;

import java.util.Set;

public class AVM
{
	private EvaluateRootCause evaluateGene;
	private long startTime;
	private String unit;

	public void AVMSearch(EvaluateRootCause evaluateGene, RootCause gene, String prop, String value)
	{
		unit = Util.getUnitFromStringValue(value);

		this.evaluateGene = evaluateGene;

		startTime = System.nanoTime();

		runAVM(evaluateGene, gene.getXpath(), prop);
	}

	public void runAVM(EvaluateRootCause evaluateGene, String xpath, String prop)
	{
		if(prop.equalsIgnoreCase("vertical-align")){
			for (String value:Constants.EXPLORATORY_VERTICAL_ALIGN_ARR){
				RootCause gene = evaluateGene.getChromosome().getGene(xpath);
				enumMove(gene,prop,value);
				if(Util.doubleArrayEqual(evaluateGene.getOptimalFitnessScore(),Constants.OPTIMAL_LOCAL_FITNESS_SCORE)){
					break;
				}
			}
//		}else if(prop.equalsIgnoreCase("left")){
//			for (String value:Constants.EXPLORATORY_LEFT_ARR){
//				RootCause gene = evaluateGene.getChromosome().getGene(xpath);
//				enumMove(gene,prop,value);
//				if(Util.doubleArrayEqual(evaluateGene.getOptimalFitnessScore(),Constants.OPTIMAL_LOCAL_FITNESS_SCORE)){
//					break;
//				}
//			}
		}else if(prop.equalsIgnoreCase("float")){
			for (String value:Constants.EXPLORATORY_FLOAT_ARR){
				RootCause gene = evaluateGene.getChromosome().getGene(xpath);
				enumMove(gene,prop,value);
				if(Util.doubleArrayEqual(evaluateGene.getOptimalFitnessScore(),Constants.OPTIMAL_LOCAL_FITNESS_SCORE)){
					break;
				}
			}
		}else {
			for (int direction : Constants.EXPLORATORY_MOVES_ARR)
			{
				RootCause gene = evaluateGene.getChromosome().getGene(xpath);
				String value = gene.getValue(prop);

				if (exploratoryMove(gene, prop, value, direction))
				{

					if (Util.doubleArrayEqual(evaluateGene.getOptimalFitnessScore(),Constants.OPTIMAL_LOCAL_FITNESS_SCORE))
					{
						break;
					}

					// do pattern moves in the direction of improvement
					patternMove(gene, prop, value, direction);

					// break this cycle of exploratory moves to reestablish a new direction
				}
			}
		}

	}
	private void enumMove(RootCause gene, String prop, String value){
		XbiFitnessFunction fitnessFunction = new XbiFitnessFunction();
		Set<String> currentXbi = evaluateGene.getOptimalPartXbi();
		double[] currentFitnessScore = evaluateGene.getOptimalFitnessScore();


		gene.updateValue(prop, value);

		System.out.println("In exploratory move with (" + gene.getXpath() + ", " + prop + ", " + value + ")");

		Set<String> newXbi = fitnessFunction.getPartXbi(evaluateGene.getChromosome(),gene);
		boolean improvement;

		double newFitnessScore[] = new double[Constants.FITNESSFUNCTION_DIMENSION];
		if(newXbi.size() <= currentXbi.size()){
			newFitnessScore = fitnessFunction.getFitnessScoreLocal(evaluateGene.getChromosome(), gene);
			improvement = true;
//			improvement = improvement ||
//					(!Util.doubleArrayTooSmall(currentFitnessScore) && Util.doubleArrayTooSmall(newFitnessScore));
		}else {
			improvement = false;
		}

//		newFitnessScore = fitnessFunction.getFitnessScoreLocal(evaluateGene.getChromosome(), gene);
//		improvement = Util.doubleArrayAbsAllLess(newFitnessScore , currentFitnessScore);

		System.out.println(improvement ? "IMPROVEMENT" : "NO IMPROVEMENT");

		// reset the value
		if (!improvement){
			System.out.println("exploratory move resetting " + prop + " value = " + evaluateGene.getOptimalValue() + " and fitness score = " + evaluateGene.getOptimalFitnessScore());
			gene.updateValue(prop, evaluateGene.getOptimalValue());
		}else{
			evaluateGene.setOptimalValue(value);
			evaluateGene.setOptimalFitnessScore(newFitnessScore);
			evaluateGene.setOptimalPartXbi(newXbi);
		}
	}
	private boolean exploratoryMove(RootCause gene, String prop, String value, int direction)
	{
		XbiFitnessFunction fitnessFunction = new XbiFitnessFunction();
		Set<String> currentXbi = evaluateGene.getOptimalPartXbi();
		double[] currentFitnessScore = evaluateGene.getOptimalFitnessScore();

		String newValue = getNewValue(prop, value, direction);
		//防止CSS取值负数
		if(CssNegative(newValue)){
			return false;
		}
		// store newValue in the chromosome
		gene.updateValue(prop, newValue);

		System.out.println("In exploratory move with (" + gene.getXpath() + ", " + prop + ", " + newValue + ", " + direction + ")");

		Set<String> newXbi = fitnessFunction.getPartXbi(evaluateGene.getChromosome(),gene);
		boolean improvement;

		double newFitnessScore[] = new double[Constants.FITNESSFUNCTION_DIMENSION];
		if(newXbi.size() <= currentXbi.size()){
			newFitnessScore = fitnessFunction.getFitnessScoreLocal(evaluateGene.getChromosome(), gene);
			improvement = Util.doubleArrayAbsAllLess(newFitnessScore , currentFitnessScore) ||
					Util.doubleArrayMaxDiff(newFitnessScore,currentFitnessScore) < 0.013;
		}else {
			improvement = false;
		}

//		double newFitnessScore[] = fitnessFunction.getFitnessScoreLocal(evaluateGene.getChromosome(), gene);
//		improvement = Util.doubleArrayAbsAllLess(newFitnessScore , currentFitnessScore);

		System.out.println(improvement ? "IMPROVEMENT" : "NO IMPROVEMENT");

		// reset the value
		if (!improvement)
		{
			System.out.println("exploratory move resetting " + prop + " value = " + evaluateGene.getOptimalValue() + " and fitness score = " + evaluateGene.getOptimalFitnessScore());
			gene.updateValue(prop, evaluateGene.getOptimalValue());
//			evaluateGene.getChromosome().setGlobalFitnessScore(evaluateGene.getOptimalFitnessScore());
		}
		else
		{
			evaluateGene.setOptimalPartXbi(newXbi); //若improve，则更新最优的XBI
			evaluateGene.setOptimalValue(newValue);
			evaluateGene.setOptimalFitnessScore(newFitnessScore);
		}

		return improvement; // true == improvement
	}

	private void patternMove(RootCause gene, String prop, String value, int direction)
	{
		XbiFitnessFunction fitnessFunction = new XbiFitnessFunction();
		double[] currentFitnessScore = evaluateGene.getOptimalFitnessScore();
		Set<String> currentXbi = evaluateGene.getOptimalPartXbi();
		boolean improvement = true;
		boolean tempTrue = false;
		boolean addOneImprovement = true;// restart to add on 1,2,4,8 pixel
		int step = 1;
		String initialValue = evaluateGene.getOptimalValue();

		while (addOneImprovement && step <40) {
			int k = Constants.PATTERN_BASE * direction;
			improvement = true;
			while ((improvement||tempTrue) && step < 40) {
				value = evaluateGene.getOptimalValue();
				currentFitnessScore = evaluateGene.getOptimalFitnessScore();
				String newValue = getNewValue(prop, value, k);

				//防止CSS取值负数
				if(CssNegative(newValue)){
					return;
				}

				// store newValue in the chromosome
				gene.updateValue(prop, newValue);
				System.out.println("In pattern move step " + step + " with (" + gene.getXpath() + ", " + prop + ", " + newValue + ", " + direction + ")");

				RootCauseList chromosome = evaluateGene.getChromosome().copy();
				chromosome.getGene(gene.getXpath()).updateValue(prop,newValue);
				Set<String> newXbi = fitnessFunction.getPartXbi(chromosome, gene);
				double newFitnessScore[] = null;
				/////////////////////////////////////////////修改比较方式

				double diffValue = 2.5;
				if (newXbi.size() <= currentXbi.size()) {
					newFitnessScore = fitnessFunction.getFitnessScoreLocal(chromosome, gene);
					improvement = Util.doubleArrayAbsAllLess(newFitnessScore, currentFitnessScore);
					tempTrue = Util.doubleArrayMaxDiff(newFitnessScore,currentFitnessScore) > diffValue
							|| Math.abs(k) <= 64;
				} else {
					improvement = false;
				}
//				double newFitnessScore[] = fitnessFunction.getFitnessScoreLocal(chromosome, gene);
//				improvement = Util.doubleArrayAbsAllLess(newFitnessScore, currentFitnessScore);

				System.out.println(improvement ? "IMPROVEMENT" : "NO IMPROVEMENT");

				if (!improvement && !tempTrue) {
					if(k == Constants.PATTERN_BASE * direction){
						addOneImprovement = false;
					}
					// reset with the last stored value
					System.out.println("pattern move resetting " + prop + " value = " + evaluateGene.getOptimalValue() + " and fitness score = " + evaluateGene.getOptimalFitnessScore());
					gene.updateValue(prop, evaluateGene.getOptimalValue());
					//				evaluateGene.getChromosome().setGlobalFitnessScore(fitnessFunction.getFitnessScoreGlobal());
				} else if(!improvement){ // tempTrue==true代表的是中间状态 不更新了
					if(k == 100){
						tempTrue = false;
					}
					if(direction == -1){
						k = Math.max(k * Constants.PATTERN_BASE,0-Constants.PATTERN_MAX);
						// multiply k 每次最大只调整100 步伐太大容易出事
					}else {
						k = Math.min(k * Constants.PATTERN_BASE,Constants.PATTERN_MAX);
					}

				}else {
					if(direction == -1){
						k = Math.max(k * Constants.PATTERN_BASE,0-Constants.PATTERN_MAX);
						// multiply k 每次最大只调整100 步伐太大容易出事
					}else {
						k = Math.min(k * Constants.PATTERN_BASE,Constants.PATTERN_MAX);
					}
					evaluateGene.setOptimalValue(newValue);
					evaluateGene.setOptimalFitnessScore(newFitnessScore);

					evaluateGene.setOptimalPartXbi(newXbi);
				}
				if(Math.abs(Util.getNumbersFromString(newValue).get(0) - Util.getNumbersFromString(initialValue).get(0)) >= Constants.ADD_MAX){
					addOneImprovement = false;
					break;
				}
				// look up time budget
				int timeElapsedInSec = (int) Util.convertNanosecondsToSeconds(System.nanoTime() - startTime);
				if (gene.getTimeBudget(prop) > -1 && timeElapsedInSec > gene.getTimeBudget(prop)) {
					System.out.println("---------------------------------------------------------------------------------------------");
					System.out.println("Terminating numeric search as the time budget of " + gene.getTimeBudget(prop) + " sec has elapsed.");
					addOneImprovement = false;
					break;
				}
				step++;
			}
		}
		System.out.println("total pattern move steps = " + step);
	}
	private boolean CssNegative(String value){
		if(value == null){
			return true;
		}
		return false;
	}

	//更新后值为负数的话，返回null
	private String getNewValue(String property, String value, int valueDelta)
	{
		int intValue = Util.getNumbersFromString(value).get(0);
		int intNewValue = intValue + valueDelta;
		if(intNewValue < 0){
			return null;
		}
		String newValue = intNewValue + unit;
		return newValue;
	}
}
